package com.wh.net.http;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.DefaultRedirectStrategy;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;

import com.wh.lang.LogUtil;
import com.wh.lang.LogicException;
import com.wh.net.NetUtil;

public class HttpClientUtil {
	public static int TIMEOUT_CONNECTION = 10000;//
	public static int TIMEOUT_SOCKET = 10000;
	public static final String UTF_8 = "UTF-8";

	public static Map<String, List<String>> getHeaders(String service) {
		HttpResponse response = null;
		HttpClient hc = null;
		try {
			hc = newHttpClient(false);
			HttpPost get = new HttpPost(service);
			response = hc.execute(get);
			Header[] headers = response.getAllHeaders();
			Map<String, List<String>> headerMap = new HashMap<String, List<String>>();
			for (Header header : headers) {
				List<String> vs = headerMap.get(header.getName());
				if (vs == null) {
					vs = new LinkedList<String>();
					headerMap.put(header.getName(), vs);
				}
				vs.add(header.getValue());
			}
			return headerMap;
		} catch (Throwable e) {
			throw new LogicException("获取上传时间出错。", e);
		} finally {
			if (response != null) try {
				EntityUtils.consume(response.getEntity());
			} catch (Throwable e) {
				if (LogUtil.rootLog.isErrorEnabled()) LogUtil.rootLog.error("释放连接出错", e);
			}
		}
	}

	public static String executeRequest(HttpClient hc, final String strURL) {
		return executeMethod(hc, new HttpGet(strURL), false);
	}

	public static byte[] executeRequest(HttpClient hc, File file, String url, boolean checkProxy) {
		byte[] in = null;
		if (file != null && file.exists()) {
			FileInputStream fin = null;
			try {
				fin = new FileInputStream(file);
				in = new byte[fin.available()];
				fin.read(in);
			} catch (Throwable e) {
				if (LogUtil.rootLog.isErrorEnabled()) LogUtil.rootLog.error("HttpUtil.executeRequest(File,String,boolean)", e);
			} finally {
				try {
					if (fin != null) fin.close();
				} catch (IOException e) {}
			}
		}
		return executeRequest(hc, url, in, checkProxy);
	}

	public static boolean isNetConnection(HttpClient hc, String url) {
		try {
			if (!NetUtil.isNetConnected()) return false;
		} catch (Throwable e) {/* 防止JNI出错的情况 */}
		HttpResponse response = null;
		try {
			if (hc == null) hc = newHttpClient(true);
			response = hc.execute(new HttpGet(url));
			checkHttpStatus(response);
			EntityUtils.consume(response.getEntity());
			return true;
		} catch (IOException e) {
			if (LogUtil.rootLog.isErrorEnabled()) LogUtil.rootLog.error("检查是否能登录网络时出错", e);
			return false;
		} finally {
			if (response != null) try {
				EntityUtils.consume(response.getEntity());
			} catch (Throwable e) {}
		}
	}

	public static byte[] executeRequest(HttpClient hc, String url, byte[] requesytBody, boolean checkProxy) {
		HttpResponse response = null;
		try {
			if (hc == null) hc = newHttpClient(checkProxy);
			HttpPost post = new HttpPost(url);
			if (requesytBody != null) post.setEntity(new ByteArrayEntity(requesytBody));
			response = hc.execute(post);
			checkHttpStatus(response);
			return EntityUtils.toByteArray(response.getEntity());
		} catch (IOException e) {
			throw new LogicException("无法连接到更新服务器，请检查您的网络设置！", e);
		} finally {
			if (response != null) try {
				EntityUtils.consume(response.getEntity());
			} catch (Throwable e) {
				if (LogUtil.rootLog.isErrorEnabled()) LogUtil.rootLog.error("释放连接出错", e);
			}
		}
	}

	public static String executeMethod(HttpClient hc, HttpRequestBase post, boolean checkProxy) {
		HttpResponse response = null;
		try {
			if (hc == null) hc = newHttpClient(checkProxy);
			response = hc.execute(post);
			checkHttpStatus(response);
			return EntityUtils.toString(response.getEntity());
		} catch (IOException e) {
			throw new LogicException("无法连接到更新服务器，请检查您的网络设置！", e);
		} finally {
			if (response != null) try {
				EntityUtils.consume(response.getEntity());
			} catch (Throwable e) {
				if (LogUtil.rootLog.isErrorEnabled()) LogUtil.rootLog.error("释放连接出错", e);
			}
		}
	}

	/**
	 * 使用handle对请求返回的数据进行处理，该方法内部实现了连接的关闭
	 * 
	 * @param request
	 *            待执行的请求
	 * @param context
	 *            当前的网络请求环境
	 * @param handle
	 *            请求返回数据的处理器，可以为null
	 */
	public static void executeMethod(HttpClient hc, HttpRequestBase request, HttpClient context, IResponseHandle handle) {
		HttpResponse response = null;
		try {
			response = context.execute(request);
			checkHttpStatus(response);
			if (handle != null) handle.process(response, request, context);
		} catch (IOException e) {
			throw new LogicException("无法连接到更新服务器，请检查您的网络设置！", e);
		} finally {
			if (response != null) try {
				EntityUtils.consume(response.getEntity());
			} catch (Throwable e) {
				if (LogUtil.rootLog.isErrorEnabled()) LogUtil.rootLog.error("释放连接出错", e);
			}
		}
	}

	public static DefaultHttpClient newHttpClient(boolean checkProxy) {
		DefaultHttpClient hc = null;
		HttpParams httpParams = new BasicHttpParams();
		HttpConnectionParams.setConnectionTimeout(httpParams, TIMEOUT_CONNECTION);
		HttpConnectionParams.setSoTimeout(httpParams, TIMEOUT_SOCKET);
		// //多连接的线程安全的管理器
		// PoolingClientConnectionManager pccm = new PoolingClientConnectionManager();
		// pccm.setDefaultMaxPerRoute(20); //每个主机的最大并行链接数
		// pccm.setMaxTotal(100); //客户端总并行链接最大数

		// 使用线程安全的连接管理来创建HttpClient
		ClientConnectionManager conMgr = new ThreadSafeClientConnManager();
		if (hc == null) hc = new DefaultHttpClient(conMgr, httpParams);
		if (checkProxy) {
			NetUtil.applyNetConfig(hc);// 应用代理设置
		}
		List<Header> defaultHeaders = new LinkedList<Header>();
		String uerAgent = "Mozilla/5.0 (Windows NT 6.1; rv:19.0) Gecko/20100101 Firefox/10.0";
		Header defaultHeader = new BasicHeader("User-Agent", uerAgent);
		defaultHeaders.add(defaultHeader);
		hc.getParams().setParameter(ClientPNames.DEFAULT_HEADERS, defaultHeaders);
		// 设置 HttpClient 接收 Cookie,用与浏览器一样的策略
		// hc.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.BROWSER_COMPATIBILITY);
		hc.setRedirectStrategy(new DefaultRedirectStrategy());
		hc.getParams().setBooleanParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, true);// 允许循环跳转
		// 设置 字符集
		// hc.setc
		hc.getParams().setParameter(HTTP.CONTENT_ENCODING, UTF_8);
		// 设置重试管理器，一次也不重试
		hc.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler(0, false));
		return hc;
	}

	public static void checkHttpStatus(HttpResponse response) {
		int httpCode = response.getStatusLine().getStatusCode();
		if (httpCode == HttpStatus.SC_OK) return;
		// LogUtil.rootLog.error("状态码：" + httpCode + response.getStatusLine().getReasonPhrase() + ",地址：" + response.g);
		LogUtil.rootLog.error("状态码：" + httpCode + "; 信息：" + response.getStatusLine().getReasonPhrase());
		// hm.releaseConnection();
		String error = ERRORS.get(httpCode);
		if (error != null) {
			throw new LogicException(error);
		} else {
			throw new LogicException("您正在请求的地址不存在，或者已经移动！");
		}
	}

	public final static Map<Integer, String> ERRORS = new HashMap<Integer, String>();

	public static String appendParams(String remoteUrl, Map<String, String> transientParams) {
		if (transientParams != null && transientParams.size() > 0) {
			StringBuilder sb = new StringBuilder(remoteUrl);
			if (sb.indexOf("?") == -1) {
				sb.append("?1=1");
			}
			for (Map.Entry<String, String> entry : transientParams.entrySet()) {
				sb.append("&").append(entry.getKey()).append("=").append(entry.getValue());
			}
			return sb.toString();
		}
		return remoteUrl;
	}
}
